Skip to content

refactor(tests): CMake-based CDB, workspace fixture, test cleanup - #378

Merged
16bit-ykiko merged 22 commits into
mainfrom
feat/compile-index-tests
Mar 31, 2026
Merged

16bit-ykiko merged 22 commits into
mainfrom
feat/compile-index-tests

Conversation

@16bit-ykiko

@16bit-ykiko 16bit-ykiko commented Mar 31, 2026 •

Copy link
Copy Markdown
Member

Summary

  • CMake-based CDB generation for module tests: Replace hand-written compile_commands.json with CMakeLists.txt (CMake 3.28 FILE_SET CXX_MODULES) in all 26 tests/data/modules/*/ directories. CDB is generated on-the-fly via cmake -G Ninja during test setup.
  • @pytest.mark.workspace() decorator: Introduce a marker + fixture pattern so tests declare their workspace via decorator and receive a resolved workspace path. The fixture auto-generates CDB when a CMakeLists.txt is present.
  • CliceClient helper methods: Add initialize(), open(), wait_diagnostics(), and open_and_wait() to reduce boilerplate across all test files.
  • Use asyncio_mode = "auto": Switch from @pytest_asyncio.fixture + @pytest.mark.asyncio to @pytest.fixture + auto mode for proper Pylance type inference on fixtures.
  • Test cleanup: Remove redundant section separators and docstrings, delete tests/pyproject.toml (config moved to pytest.ini).
  • Format task: Add .cppm to format-cpp glob pattern.
  • CI fix: Disable CMAKE_CXX_SCAN_FOR_MODULES and prefer pixi clang++ to fix macOS CI where CMake rejects module scanning.

Test plan

  • All 26 module test directories have CMakeLists.txt with FILE_SET CXX_MODULES
  • generate_cdb() produces valid compile_commands.json with module flags
  • Integration tests pass locally
  • CI passes on all platforms (Linux, macOS, Windows)

Summary by CodeRabbit

  • Tests
    • Unified fixtures and client workflow: new init/open/wait helpers, workspace marker support, bounded diagnostics waiting, CMake-based compilation-database generation, and directory-backed temp-file workflows; enabled asyncio test mode.
  • Chores
    • Added many C++20 module test projects and test data; removed prior test pyproject in favor of pytest config; updated formatter to include .cppm files.
  • Style
    • Reformatted many module/source implementations to consistent multi-line function bodies.

@coderabbitai

coderabbitai Bot commented Mar 31, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Consolidates test LSP tooling: adds typed CliceClient APIs (initialize/open/wait), workspace fixture and CDB generator (CMake-backed), migrates tests from TempFile→TempDir with shared write_cdb, adds many CMake module test projects and .cppm formatting, and moves pytest asyncio config to pytest.ini.

Changes

Cohort / File(s) Summary
Test harness & client
tests/conftest.py, tests/pytest.ini
Adds typed CliceClient state and async methods (init_result, initialize, open, wait_diagnostics, open_and_wait), converts client to an async @pytest.fixture, adds workspace fixture and generate_cdb, and configures pytest asyncio/workspace marker.
Integration tests
tests/integration/.../test_file_operation.py, tests/integration/.../test_lifecycle.py, tests/integration/.../test_modules.py, tests/integration/.../test_server.py
Rewrites tests to use @pytest.mark.workspace + workspace fixture and CliceClient helpers; removes per-test init/open helpers and protocol-type imports; assertions updated to read from client.init_result.
Compile DB helper & tests
tests/unit/test/cdb_helper.h, tests/unit/syntax/.../dependency_graph_tests.cpp, tests/unit/server/compile_graph_integration_tests.cpp
Adds shared clice::testing::write_cdb and removes duplicate local write_cdb implementations; callers now rely on shared helper.
TempFile → TempDir migration
tests/unit/server/worker_test_helpers.h, tests/unit/server/module_worker_tests.cpp, tests/unit/server/stateful_worker_tests.cpp, tests/unit/server/stateless_worker_tests.cpp
Removes TempFile helper and its header usage; tests now create files via TempDir::touch() and pass filesystem paths (tmp.path(...)) into worker/compile params.
Integration module fixtures (.cppm + CMake)
tests/data/modules/*/CMakeLists.txt, tests/data/modules/**/*.cppm
Adds many module CMakeLists (C++20, CMAKE_EXPORT_COMPILE_COMMANDS, FILE_SET CXX_MODULES) and reformats .cppm sources (mostly single-line→multi-line bodies); no API behavior changes.
Project/test config & formatting
tests/pyproject.toml, pixi.toml
Removes tests/pyproject.toml; adds tests/pytest.ini; updates pixi.toml format-cpp task to include .cppm files.
Build/toolchain changes
CMakeLists.txt, cmake/toolchain.cmake, scripts/activate_asan.bat
Adjusts AddressSanitizer linkage logic and switches Windows clang driver selection to non--cl clang toolchain with target/ld flags; updates clang resource-dir discovery to clang++.

Sequence Diagram(s)

sequenceDiagram
  participant Tester as Tester
  participant FS as FileSystem
  participant Client as CliceClient
  participant Server as LSP Server

  Tester->>Client: initialize(workspace: Path)
  Client->>Server: initialize(request with rootUri/workspaceFolders)
  Server-->>Client: InitializeResult
  Client->>Server: initialized notification
  Tester->>FS: read(filepath)
  FS-->>Client: content, uri
  Client->>Server: textDocument/didOpen (uri, content)
  Server->>Server: analyze / compile (may invoke CMake/Ninja for CDB)
  Server-->>Client: textDocument/publishDiagnostics (uri)
  Client-->>Tester: wait_diagnostics returns
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Poem

🐇 I hopped through fixtures, built a client bright,

I opened files, waited for diagnostics by night,
CMake planted compile commands where modules now play,
TempDir kept my crumbs — old TempFile bounced away,
Tests chirp, green and ready — a carrot-patch delight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: refactoring tests with CMake-based compilation database, workspace fixture additions, and test cleanup.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/compile-index-tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- Remove TempFile from worker_test_helpers.h, use TempDir from temp_dir.h
  for isolated temp directories per test case
- Move write_cdb() into cdb_helper.h to deduplicate across
  dependency_graph_tests and compile_graph_integration_tests
- Extract shared LSP helpers (lsp_initialize, lsp_open, lsp_open_and_wait,
  lsp_wait_diagnostics) into conftest.py, removing duplicate _init/_open/
  _open_and_wait from test_server.py, test_file_operation.py, test_modules.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (5)
tests/conftest.py (1)

199-210: Consider parameterizing language_id for broader file type support.

The helper hardcodes language_id="cpp" for all files. While this works for .cpp files, the module tests in test_modules.py open .cppm files through this helper. If the server relies on language_id for module-specific processing, this could cause issues.

Consider allowing callers to override the language ID when needed:

♻️ Optional: Accept language_id parameter
-def lsp_open(client, filepath: Path, version: int = 0):
+def lsp_open(client, filepath: Path, version: int = 0, language_id: str = "cpp"):
     """Open a text document and return (uri, content)."""
     content = filepath.read_text(encoding="utf-8")
     uri = filepath.as_uri()
     client.text_document_did_open(
         DidOpenTextDocumentParams(
             text_document=TextDocumentItem(
-                uri=uri, language_id="cpp", version=version, text=content
+                uri=uri, language_id=language_id, version=version, text=content
             )
         )
     )
     return uri, content
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/conftest.py` around lines 199 - 210, The helper lsp_open currently
hardcodes language_id="cpp"; change its signature to accept an optional
language_id parameter (default "cpp") and pass that value into TextDocumentItem
when calling client.text_document_did_open so callers can override for .cppm or
other files; update any tests that call lsp_open to pass the appropriate
language_id where needed (references: lsp_open function and TextDocumentItem in
tests/conftest.py).
tests/unit/feature/code_completion_tests.cpp (1)

17-31: Prefer function-local vfs/main_path to avoid shared mutable test state.

Keeping these at suite scope is unnecessary and can make parallel runs flaky. Localize both inside code_complete().

Refactor sketch
-llvm::IntrusiveRefCntPtr<TestVFS> vfs;
-std::string main_path;
@@
 void code_complete(llvm::StringRef code) {
-    vfs = llvm::makeIntrusiveRefCnt<TestVFS>();
+    auto vfs = llvm::makeIntrusiveRefCnt<TestVFS>();
@@
-    main_path = TestVFS::path("main.cpp");
+    auto main_path = TestVFS::path("main.cpp");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/feature/code_completion_tests.cpp` around lines 17 - 31, The
global mutable variables vfs and main_path should be made local to
code_complete() to avoid shared test state; remove the suite-scope declarations
of llvm::IntrusiveRefCntPtr<TestVFS> vfs and std::string main_path and instead
create local variables inside code_complete() (e.g.,
llvm::IntrusiveRefCntPtr<TestVFS> vfs = llvm::makeIntrusiveRefCnt<TestVFS>();
and std::string main_path = TestVFS::path("main.cpp")), update uses in the
function where CompilationParams params, AnnotatedSource::from(code),
vfs->add(...), params.vfs = vfs, params.arguments and params.completion
reference them, and ensure no other tests rely on the removed globals.
tests/unit/compile/compilation_tests.cpp (2)

242-273: Test does not verify PCH reuse with the second content version.

The test computes bounds for both content_v1 and content_v2 and verifies they're equal, but only compiles v1. To fully test that "modifying code after the preamble should not require PCH rebuild," the test should also compile v2 using the existing PCH and verify it succeeds.

♻️ Suggested enhancement to complete the test
     // Build PCH with v1.
     add_main("main.cpp", content_v1);
     ASSERT_TRUE(compile_with_pch());
     ASSERT_TRUE(unit.has_value());
     ASSERT_TRUE(unit->top_level_decls().size() >= 1U);
+
+    // Verify v2 can reuse the same PCH (bound is identical).
+    // Note: This would require exposing the PCH or modifying compile_with_pch
+    // to accept pre-built PCH info, which may be out of scope for this PR.
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/compile/compilation_tests.cpp` around lines 242 - 273, The test
PCHContentDifference computes preamble bounds for content_v1 and content_v2 but
never verifies PCH reuse for content_v2; after building the PCH with content_v1
(using add_main("main.cpp", content_v1) and ASSERT_TRUE(compile_with_pch())),
update the test to replace the main with content_v2 (e.g., call
add_main("main.cpp", content_v2)), invoke compile_with_pch() again and
ASSERT_TRUE on its result, then assert unit.has_value() and that
unit->top_level_decls().size() is >= 1U to confirm compilation succeeded without
rebuilding PCH.

175-240: Test cleanup may leave orphaned files on early assertion failure.

The temp PCM files created via fs::createTemporaryFile are only cleaned up at the end of the test. If any ASSERT_* fails before lines 238-239, the files will remain. Consider using RAII-based cleanup or moving cleanup to a scope guard.

♻️ Optional: RAII-based cleanup pattern
+    // Helper for automatic cleanup
+    auto cleanup_file = [](const std::string& path) {
+        llvm::sys::fs::remove(path);
+    };
+    std::unique_ptr<std::string, decltype(cleanup_file)> pcm_b_guard;
+    std::unique_ptr<std::string, decltype(cleanup_file)> pcm_a_guard;
+
     auto pcm_b_path = fs::createTemporaryFile("mod_b", "pcm");
     ASSERT_TRUE(pcm_b_path.operator bool());
+    pcm_b_guard = std::unique_ptr<std::string, decltype(cleanup_file)>(
+        new std::string(*pcm_b_path), cleanup_file);
     params_b.output_file = *pcm_b_path;
     // ... similar for pcm_a_path ...
-
-    // Clean up temp PCM files.
-    llvm::sys::fs::remove(*pcm_b_path);
-    llvm::sys::fs::remove(*pcm_a_path);

Alternatively, if the test framework supports it, use a test fixture with teardown.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/compile/compilation_tests.cpp` around lines 175 - 240, The test
currently creates temporary PCM files via fs::createTemporaryFile (pcm_b_path,
pcm_a_path) and only calls llvm::sys::fs::remove at the end, which leaks files
on early ASSERT_* failures; wrap the temporary-paths in an RAII cleanup (e.g., a
small ScopedTempFile or scope guard) that stores the Optional<Path> returned by
fs::createTemporaryFile and removes the file in its destructor, or register a
teardown/scope-exit that always calls llvm::sys::fs::remove for pcm_b_path and
pcm_a_path so cleanup runs even if unit_b or unit_a assertions fail.
tests/unit/test/tester.cpp (1)

201-281: Consider extracting common setup logic to reduce duplication.

The initial setup (lines 202-217) duplicates prepare_driver()'s argument resolution logic. Similarly, the file remapping loops (lines 235-243 and 262-269) share identical path-handling logic. While the current implementation is correct, extracting a helper could improve maintainability.

♻️ Optional: Extract shared logic
// Private helper for driver argument setup
void Tester::setup_driver_args(llvm::StringRef standard) {
    params = CompilationParams();
    unit.reset();
    vfs = llvm::makeIntrusiveRefCnt<TestVFS>();
    for(auto& [file, source]: sources.all_files) {
        vfs->add(file, source.content);
    }

    auto command = std::format("clang++ {} {} -fms-extensions", standard, src_path);
    database.add_command("fake", src_path, command);

    CommandOptions options;
    options.query_toolchain = true;
    options.suppress_logging = true;
    auto commands = database.lookup(src_path, options);
    assert(!commands.empty() && "lookup failed after add_command");
    params.arguments = commands.front().arguments;
}

// Private helper for remapping files
void Tester::remap_sources(std::optional<std::size_t> main_bound = std::nullopt) {
    for(auto& [file, source]: sources.all_files) {
        if(file == src_path) {
            if(main_bound) {
                params.add_remapped_file(file, source.content, *main_bound);
            } else {
                params.add_remapped_file(file, source.content);
            }
        } else {
            std::string path = path::is_absolute(file) ? file.str() : path::join(".", file);
            params.add_remapped_file(path, source.content);
        }
    }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/test/tester.cpp` around lines 201 - 281, The method
Tester::compile_driver_with_pch duplicates driver argument resolution and
file-remapping logic; extract the initial setup block (params reset, unit.reset,
vfs creation and population, command creation/database.add_command,
CommandOptions lookup and params.arguments assignment) into a new private helper
Tester::setup_driver_args(llvm::StringRef standard) and replace the duplicated
block with a call to it, and extract the two remapping loops into a private
Tester::remap_sources(std::optional<std::size_t> main_bound = std::nullopt) that
adds remapped files (using path::is_absolute/file.str or path::join for
non-src_path entries and the optional bound for the main file); then call
remap_sources(bound) for the preamble phase and remap_sources() for the content
phase, and update any other callers like prepare_driver() to use
setup_driver_args.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/semantic/ast_utility.cpp`:
- Around line 892-895: The detection logic that treats any declaration under a
top-level std ancestor as std::forward is too broad; change the loop that walks
DeclContext (used by the std::forward check and by unwrapForward) to ensure
every namespace between the callee and the top-level "std" is an inline
namespace. Concretely, when iterating DC = Callee->getDeclContext() upwards and
llvm::dyn_casting to clang::NamespaceDecl, require NS->isInline() for every
intermediate namespace (allowing the final match where identifier_of(*NS) ==
"std" and NS->getParent()->isTranslationUnit()); only return true if that
inline-namespace chain condition holds. Update the same check used by
unwrapForward accordingly.

In `@tests/integration/test_modules.py`:
- Line 22: Remove the unused import lsp_wait_diagnostics from the import list in
tests/integration/test_modules.py: keep only the actually used helpers
(lsp_initialize, lsp_open, lsp_open_and_wait) since diagnostics are already
awaited via lsp_open_and_wait; update the import statement to eliminate
lsp_wait_diagnostics to resolve the unused-import warning.

In `@tests/integration/test_server.py`:
- Line 30: Remove the unused import lsp_wait_diagnostics from the import list in
tests/integration/test_server.py; update the line that currently imports
lsp_initialize, lsp_open, lsp_open_and_wait, lsp_wait_diagnostics to only import
lsp_initialize, lsp_open, lsp_open_and_wait (since lsp_open_and_wait already
calls lsp_wait_diagnostics), then run the test suite to ensure no regressions.

In `@tests/unit/feature/signature_help_tests.cpp`:
- Around line 19-21: The test indexes nameless_points()[0] without checking that
nameless_points() contains any markers, which can crash on malformed input; add
a guard before using nameless_points()[0] (e.g., assert or conditional) in the
test setup where params.completion is assigned so that if
nameless_points().empty() you fail the test with a clear message or skip setting
params.completion, and ensure the rest of the setup lines
(TestVFS::path("main.cpp"), params.add_remapped_file(...),
sources.all_files["main.cpp"].content) are only executed when a valid marker
exists.

In `@tests/unit/test/cdb_helper.h`:
- Around line 22-31: The json_escape function only handles backslash and
double-quote and must also escape JSON control characters; update inline
std::string json_escape(llvm::StringRef s) to map control chars (0x00–0x1F) to
their JSON-escaped forms (use "\b", "\f", "\n", "\r", "\t" for those names and
otherwise "\u00XX" hex escapes for other control bytes) while still escaping
'\\' and '"' so generated compile_commands.json contains valid JSON string
values.

---

Nitpick comments:
In `@tests/conftest.py`:
- Around line 199-210: The helper lsp_open currently hardcodes
language_id="cpp"; change its signature to accept an optional language_id
parameter (default "cpp") and pass that value into TextDocumentItem when calling
client.text_document_did_open so callers can override for .cppm or other files;
update any tests that call lsp_open to pass the appropriate language_id where
needed (references: lsp_open function and TextDocumentItem in
tests/conftest.py).

In `@tests/unit/compile/compilation_tests.cpp`:
- Around line 242-273: The test PCHContentDifference computes preamble bounds
for content_v1 and content_v2 but never verifies PCH reuse for content_v2; after
building the PCH with content_v1 (using add_main("main.cpp", content_v1) and
ASSERT_TRUE(compile_with_pch())), update the test to replace the main with
content_v2 (e.g., call add_main("main.cpp", content_v2)), invoke
compile_with_pch() again and ASSERT_TRUE on its result, then assert
unit.has_value() and that unit->top_level_decls().size() is >= 1U to confirm
compilation succeeded without rebuilding PCH.
- Around line 175-240: The test currently creates temporary PCM files via
fs::createTemporaryFile (pcm_b_path, pcm_a_path) and only calls
llvm::sys::fs::remove at the end, which leaks files on early ASSERT_* failures;
wrap the temporary-paths in an RAII cleanup (e.g., a small ScopedTempFile or
scope guard) that stores the Optional<Path> returned by fs::createTemporaryFile
and removes the file in its destructor, or register a teardown/scope-exit that
always calls llvm::sys::fs::remove for pcm_b_path and pcm_a_path so cleanup runs
even if unit_b or unit_a assertions fail.

In `@tests/unit/feature/code_completion_tests.cpp`:
- Around line 17-31: The global mutable variables vfs and main_path should be
made local to code_complete() to avoid shared test state; remove the suite-scope
declarations of llvm::IntrusiveRefCntPtr<TestVFS> vfs and std::string main_path
and instead create local variables inside code_complete() (e.g.,
llvm::IntrusiveRefCntPtr<TestVFS> vfs = llvm::makeIntrusiveRefCnt<TestVFS>();
and std::string main_path = TestVFS::path("main.cpp")), update uses in the
function where CompilationParams params, AnnotatedSource::from(code),
vfs->add(...), params.vfs = vfs, params.arguments and params.completion
reference them, and ensure no other tests rely on the removed globals.

In `@tests/unit/test/tester.cpp`:
- Around line 201-281: The method Tester::compile_driver_with_pch duplicates
driver argument resolution and file-remapping logic; extract the initial setup
block (params reset, unit.reset, vfs creation and population, command
creation/database.add_command, CommandOptions lookup and params.arguments
assignment) into a new private helper Tester::setup_driver_args(llvm::StringRef
standard) and replace the duplicated block with a call to it, and extract the
two remapping loops into a private
Tester::remap_sources(std::optional<std::size_t> main_bound = std::nullopt) that
adds remapped files (using path::is_absolute/file.str or path::join for
non-src_path entries and the optional bound for the main file); then call
remap_sources(bound) for the preamble phase and remap_sources() for the content
phase, and update any other callers like prepare_driver() to use
setup_driver_args.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: db36cd05-db0e-4fae-8488-e0710a99d19e

📥 Commits

Reviewing files that changed from the base of the PR and between 0a891d8 and b64ee8d.

📒 Files selected for processing (36)
  • src/semantic/ast_utility.cpp
  • tests/conftest.py
  • tests/integration/test_file_operation.py
  • tests/integration/test_lifecycle.py
  • tests/integration/test_modules.py
  • tests/integration/test_server.py
  • tests/unit/command/argument_parser_tests.cpp
  • tests/unit/command/command_tests.cpp
  • tests/unit/command/toolchain_tests.cpp
  • tests/unit/compile/compilation_tests.cpp
  • tests/unit/compile/diagnostic_tests.cpp
  • tests/unit/compile/directive_tests.cpp
  • tests/unit/compile/tidy_tests.cpp
  • tests/unit/feature/code_completion_tests.cpp
  • tests/unit/feature/document_link_tests.cpp
  • tests/unit/feature/document_symbol_tests.cpp
  • tests/unit/feature/folding_range_tests.cpp
  • tests/unit/feature/hover_tests.cpp
  • tests/unit/feature/inlay_hint_tests.cpp
  • tests/unit/feature/semantic_tokens_tests.cpp
  • tests/unit/feature/signature_help_tests.cpp
  • tests/unit/index/merged_index_tests.cpp
  • tests/unit/index/project_index_tests.cpp
  • tests/unit/index/tu_index_tests.cpp
  • tests/unit/semantic/selection_tests.cpp
  • tests/unit/semantic/template_resolver_tests.cpp
  • tests/unit/server/compile_graph_integration_tests.cpp
  • tests/unit/server/module_worker_tests.cpp
  • tests/unit/server/stateful_worker_tests.cpp
  • tests/unit/server/stateless_worker_tests.cpp
  • tests/unit/server/worker_test_helpers.h
  • tests/unit/syntax/dependency_graph_tests.cpp
  • tests/unit/test/annotation.h
  • tests/unit/test/cdb_helper.h
  • tests/unit/test/tester.cpp
  • tests/unit/test/tester.h

Comment thread src/semantic/ast_utility.cpp
Comment thread tests/integration/test_modules.py Outdated
Comment thread tests/integration/test_server.py Outdated
Comment thread tests/unit/feature/signature_help_tests.cpp
Comment thread tests/unit/test/cdb_helper.h
@16bit-ykiko
16bit-ykiko force-pushed the feat/compile-index-tests branch from b64ee8d to 5fdd857 Compare March 31, 2026 03:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/unit/test/cdb_helper.h (1)

62-65: Expose load() result to avoid silent test setup failures.

On Line 64, the loaded-entry count is discarded. Returning it from write_cdb(...) makes failures easier to catch at call sites (especially when JSON is malformed or file creation fails).

Suggested diff
-inline void write_cdb(TempDir& tmp, CompilationDatabase& cdb, llvm::StringRef json_content) {
+inline std::size_t write_cdb(TempDir& tmp, CompilationDatabase& cdb, llvm::StringRef json_content) {
     tmp.touch("compile_commands.json", json_content);
-    cdb.load(tmp.path("compile_commands.json"));
+    return cdb.load(tmp.path("compile_commands.json"));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/test/cdb_helper.h` around lines 62 - 65, The helper write_cdb
currently discards the return value of CompilationDatabase::load which can hide
test setup failures; change write_cdb(TempDir& tmp, CompilationDatabase& cdb,
llvm::StringRef json_content) to return the load result (e.g., an int or size_t)
and propagate the value from cdb.load(tmp.path("compile_commands.json")) so
callers can assert the loaded-entry count after creating the
compile_commands.json file via TempDir::touch; ensure callers in tests are
updated to check the returned count where appropriate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/unit/test/cdb_helper.h`:
- Around line 62-65: The helper write_cdb currently discards the return value of
CompilationDatabase::load which can hide test setup failures; change
write_cdb(TempDir& tmp, CompilationDatabase& cdb, llvm::StringRef json_content)
to return the load result (e.g., an int or size_t) and propagate the value from
cdb.load(tmp.path("compile_commands.json")) so callers can assert the
loaded-entry count after creating the compile_commands.json file via
TempDir::touch; ensure callers in tests are updated to check the returned count
where appropriate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e19c34fb-8b8e-4eda-8d80-736227b52429

📥 Commits

Reviewing files that changed from the base of the PR and between b64ee8d and 5fdd857.

📒 Files selected for processing (12)
  • tests/conftest.py
  • tests/integration/test_file_operation.py
  • tests/integration/test_lifecycle.py
  • tests/integration/test_modules.py
  • tests/integration/test_server.py
  • tests/unit/server/compile_graph_integration_tests.cpp
  • tests/unit/server/module_worker_tests.cpp
  • tests/unit/server/stateful_worker_tests.cpp
  • tests/unit/server/stateless_worker_tests.cpp
  • tests/unit/server/worker_test_helpers.h
  • tests/unit/syntax/dependency_graph_tests.cpp
  • tests/unit/test/cdb_helper.h
💤 Files with no reviewable changes (2)
  • tests/unit/server/compile_graph_integration_tests.cpp
  • tests/unit/syntax/dependency_graph_tests.cpp
✅ Files skipped from review due to trivial changes (1)
  • tests/unit/server/module_worker_tests.cpp
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/integration/test_lifecycle.py
  • tests/unit/server/worker_test_helpers.h
  • tests/integration/test_modules.py
  • tests/conftest.py
  • tests/unit/server/stateful_worker_tests.cpp

Move lsp_initialize/lsp_open/lsp_open_and_wait/lsp_wait_diagnostics
from free functions into CliceClient as initialize/open/open_and_wait/
wait_diagnostics methods for cleaner API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/conftest.py`:
- Around line 122-126: The method open_and_wait calls self.open(filepath) before
setting up the diagnostics wait, causing races if diagnostics arrive
immediately; change the flow in open_and_wait so you prepare/subscribe the
diagnostics wait (i.e., call or create the wait event via self.wait_diagnostics
or equivalent subscription) before invoking self.open(filepath), then open the
file and await the previously prepared diagnostics future; update references in
tests/conftest.py to ensure open_and_wait uses the pre-created diagnostics
waiter (and that on_diagnostics sets that event) to avoid intermittent timeouts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4a357c11-3df7-4cb8-8a8e-7feb0b63b1f9

📥 Commits

Reviewing files that changed from the base of the PR and between 5fdd857 and 3241c5e.

📒 Files selected for processing (5)
  • tests/conftest.py
  • tests/integration/test_file_operation.py
  • tests/integration/test_lifecycle.py
  • tests/integration/test_modules.py
  • tests/integration/test_server.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/integration/test_lifecycle.py
  • tests/integration/test_server.py
  • tests/integration/test_file_operation.py

Comment thread tests/conftest.py Outdated
Replace hand-written compile_commands.json with per-directory
CMakeLists.txt and cmake -G Ninja generation for more realistic
integration test coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
tests/data/modules/independent_modules/CMakeLists.txt (1)

4-5: Make the C++ standard non-negotiable in this fixture.

Please add CMAKE_CXX_STANDARD_REQUIRED ON to avoid silent downgrade from C++20.

Suggested patch
 set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/data/modules/independent_modules/CMakeLists.txt` around lines 4 - 5,
Add an explicit requirement that the C++ standard cannot be downgraded by
setting CMAKE_CXX_STANDARD_REQUIRED ON in the CMakeLists fixture: locate the
existing set(CMAKE_CXX_STANDARD 20) (and the related set(CMAKE_CXX_EXTENSIONS
OFF)) and insert a set(CMAKE_CXX_STANDARD_REQUIRED ON) immediately after them so
CMake enforces C++20.
tests/data/modules/deep_chain/CMakeLists.txt (1)

4-5: Require C++20 explicitly to keep fixture behavior deterministic.

Add CMAKE_CXX_STANDARD_REQUIRED ON so CI/toolchain differences can’t downgrade the language mode.

Suggested patch
 set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/data/modules/deep_chain/CMakeLists.txt` around lines 4 - 5, Add an
explicit requirement that the C++ standard not be downgraded by setting
CMAKE_CXX_STANDARD_REQUIRED ON in the same CMakeLists context that defines the
standard (near set(CMAKE_CXX_STANDARD 20) and set(CMAKE_CXX_EXTENSIONS OFF));
this ensures the C++20 mode is enforced across CI/toolchains by making
CMAKE_CXX_STANDARD required rather than optional.
tests/data/modules/module_partitions/CMakeLists.txt (1)

4-5: Enforce C++20 as a hard requirement for this fixture.

Set CMAKE_CXX_STANDARD_REQUIRED ON so this test fixture never silently falls back to an older standard on unsupported toolchains.

Suggested patch
 set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/data/modules/module_partitions/CMakeLists.txt` around lines 4 - 5, The
CMakeLists currently sets CMAKE_CXX_STANDARD to 20 but doesn't enforce it;
update the fixture to require C++20 by adding a directive to enable strict
standard enforcement—specifically add CMAKE_CXX_STANDARD_REQUIRED set to ON (in
the same scope where set(CMAKE_CXX_STANDARD 20) and set(CMAKE_CXX_EXTENSIONS
OFF) are defined) so the build will fail rather than silently falling back on
older standards.
tests/data/modules/dotted_module_name/CMakeLists.txt (1)

4-5: Apply strict C++20 enforcement here as well.

Recommend adding CMAKE_CXX_STANDARD_REQUIRED ON for consistent fixture behavior across environments.

Suggested patch
 set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/data/modules/dotted_module_name/CMakeLists.txt` around lines 4 - 5, The
CMakeLists currently sets CMAKE_CXX_STANDARD and CMAKE_CXX_EXTENSIONS but
doesn't enforce the standard; update the file to explicitly require C++20 by
adding set(CMAKE_CXX_STANDARD_REQUIRED ON) so that the CMake target honors
CMAKE_CXX_STANDARD (refer to the existing set(CMAKE_CXX_STANDARD 20) and
set(CMAKE_CXX_EXTENSIONS OFF) lines) and ensures consistent fixture behavior
across environments.
tests/integration/test_modules.py (1)

53-54: Replace fixed sleep with readiness-based synchronization.

A hardcoded sleep(2.0) can be flaky and adds cumulative test latency. Prefer waiting on a concrete readiness signal/event from the client/server handshake path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/test_modules.py` around lines 53 - 54, Replace the brittle
hardcoded await asyncio.sleep(2.0) with a readiness-based wait: remove the sleep
and await a concrete readiness signal such as a client/server handshake
completion API or event (for example await client.wait_until_ready() or await
server_ready_event.wait()), or add a small helper like wait_for_ready() that
polls/awaits the connection/handshake state (e.g., client.handshake_complete or
server.signaled_ready) so the test proceeds only when the server is truly ready.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/integration/test_modules.py`:
- Around line 35-46: Replace the literal "cmake" in the subprocess.run call with
an absolute path resolved via shutil.which("cmake") and fail-fast if it returns
None; update the argument list passed to subprocess.run (the call in
tests/integration/test_modules.py where subprocess.run(...) builds the CMake
command) to use that resolved path instead of the string, and add a timeout
parameter (e.g., timeout=60) to the subprocess.run invocation to avoid hanging
in CI; ensure you do not enable shell=True and keep check=True and
capture_output/Text as before.

---

Nitpick comments:
In `@tests/data/modules/deep_chain/CMakeLists.txt`:
- Around line 4-5: Add an explicit requirement that the C++ standard not be
downgraded by setting CMAKE_CXX_STANDARD_REQUIRED ON in the same CMakeLists
context that defines the standard (near set(CMAKE_CXX_STANDARD 20) and
set(CMAKE_CXX_EXTENSIONS OFF)); this ensures the C++20 mode is enforced across
CI/toolchains by making CMAKE_CXX_STANDARD required rather than optional.

In `@tests/data/modules/dotted_module_name/CMakeLists.txt`:
- Around line 4-5: The CMakeLists currently sets CMAKE_CXX_STANDARD and
CMAKE_CXX_EXTENSIONS but doesn't enforce the standard; update the file to
explicitly require C++20 by adding set(CMAKE_CXX_STANDARD_REQUIRED ON) so that
the CMake target honors CMAKE_CXX_STANDARD (refer to the existing
set(CMAKE_CXX_STANDARD 20) and set(CMAKE_CXX_EXTENSIONS OFF) lines) and ensures
consistent fixture behavior across environments.

In `@tests/data/modules/independent_modules/CMakeLists.txt`:
- Around line 4-5: Add an explicit requirement that the C++ standard cannot be
downgraded by setting CMAKE_CXX_STANDARD_REQUIRED ON in the CMakeLists fixture:
locate the existing set(CMAKE_CXX_STANDARD 20) (and the related
set(CMAKE_CXX_EXTENSIONS OFF)) and insert a set(CMAKE_CXX_STANDARD_REQUIRED ON)
immediately after them so CMake enforces C++20.

In `@tests/data/modules/module_partitions/CMakeLists.txt`:
- Around line 4-5: The CMakeLists currently sets CMAKE_CXX_STANDARD to 20 but
doesn't enforce it; update the fixture to require C++20 by adding a directive to
enable strict standard enforcement—specifically add CMAKE_CXX_STANDARD_REQUIRED
set to ON (in the same scope where set(CMAKE_CXX_STANDARD 20) and
set(CMAKE_CXX_EXTENSIONS OFF) are defined) so the build will fail rather than
silently falling back on older standards.

In `@tests/integration/test_modules.py`:
- Around line 53-54: Replace the brittle hardcoded await asyncio.sleep(2.0) with
a readiness-based wait: remove the sleep and await a concrete readiness signal
such as a client/server handshake completion API or event (for example await
client.wait_until_ready() or await server_ready_event.wait()), or add a small
helper like wait_for_ready() that polls/awaits the connection/handshake state
(e.g., client.handshake_complete or server.signaled_ready) so the test proceeds
only when the server is truly ready.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6285a383-aa28-4da8-86e8-fcf51c3e1282

📥 Commits

Reviewing files that changed from the base of the PR and between 3241c5e and e5e60f5.

📒 Files selected for processing (27)
  • tests/data/modules/chained_modules/CMakeLists.txt
  • tests/data/modules/circular_module_dependency/CMakeLists.txt
  • tests/data/modules/class_export_and_inheritance/CMakeLists.txt
  • tests/data/modules/consumer_imports_module/CMakeLists.txt
  • tests/data/modules/deep_chain/CMakeLists.txt
  • tests/data/modules/diamond_modules/CMakeLists.txt
  • tests/data/modules/dotted_module_name/CMakeLists.txt
  • tests/data/modules/export_block/CMakeLists.txt
  • tests/data/modules/export_namespace/CMakeLists.txt
  • tests/data/modules/global_module_fragment/CMakeLists.txt
  • tests/data/modules/gmf_with_import/CMakeLists.txt
  • tests/data/modules/hover_on_imported_symbol/CMakeLists.txt
  • tests/data/modules/independent_modules/CMakeLists.txt
  • tests/data/modules/module_compile_error/CMakeLists.txt
  • tests/data/modules/module_implementation_unit/CMakeLists.txt
  • tests/data/modules/module_partitions/CMakeLists.txt
  • tests/data/modules/no_modules_plain_cpp/CMakeLists.txt
  • tests/data/modules/partition_chain/CMakeLists.txt
  • tests/data/modules/partition_interface/CMakeLists.txt
  • tests/data/modules/partition_with_external_import/CMakeLists.txt
  • tests/data/modules/partition_with_gmf/CMakeLists.txt
  • tests/data/modules/private_module_fragment/CMakeLists.txt
  • tests/data/modules/re_export/CMakeLists.txt
  • tests/data/modules/save_recompile/CMakeLists.txt
  • tests/data/modules/single_module_no_deps/CMakeLists.txt
  • tests/data/modules/template_export/CMakeLists.txt
  • tests/integration/test_modules.py
✅ Files skipped from review due to trivial changes (22)
  • tests/data/modules/partition_with_external_import/CMakeLists.txt
  • tests/data/modules/global_module_fragment/CMakeLists.txt
  • tests/data/modules/consumer_imports_module/CMakeLists.txt
  • tests/data/modules/circular_module_dependency/CMakeLists.txt
  • tests/data/modules/hover_on_imported_symbol/CMakeLists.txt
  • tests/data/modules/template_export/CMakeLists.txt
  • tests/data/modules/partition_chain/CMakeLists.txt
  • tests/data/modules/module_implementation_unit/CMakeLists.txt
  • tests/data/modules/no_modules_plain_cpp/CMakeLists.txt
  • tests/data/modules/export_namespace/CMakeLists.txt
  • tests/data/modules/save_recompile/CMakeLists.txt
  • tests/data/modules/chained_modules/CMakeLists.txt
  • tests/data/modules/export_block/CMakeLists.txt
  • tests/data/modules/single_module_no_deps/CMakeLists.txt
  • tests/data/modules/partition_with_gmf/CMakeLists.txt
  • tests/data/modules/diamond_modules/CMakeLists.txt
  • tests/data/modules/gmf_with_import/CMakeLists.txt
  • tests/data/modules/private_module_fragment/CMakeLists.txt
  • tests/data/modules/re_export/CMakeLists.txt
  • tests/data/modules/class_export_and_inheritance/CMakeLists.txt
  • tests/data/modules/partition_interface/CMakeLists.txt
  • tests/data/modules/module_compile_error/CMakeLists.txt

Comment thread tests/integration/test_modules.py Outdated
- Resolve cmake path via shutil.which() and add subprocess timeout
- Fix race in open_and_wait: arm diagnostics event before opening doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
tests/integration/test_modules.py (1)

400-402: ⚠️ Potential issue | 🟠 Major

Line 400 reintroduces the open/diagnostics race.

Line 400 sends didOpen before the waiter exists, so a fast publish can be missed and Line 402 will intermittently time out. Reuse client.open_and_wait() here, or arm wait_for_diagnostics() before client.open().

🔧 Suggested patch
-    leaf_uri, _ = client.open(tmp_path / "leaf.cppm")
-    event = client.wait_for_diagnostics(leaf_uri)
-    await asyncio.wait_for(event.wait(), timeout=60.0)
+    leaf_uri, _ = await client.open_and_wait(tmp_path / "leaf.cppm")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/test_modules.py` around lines 400 - 402, The test
reintroduces an open/diagnostics race by calling client.open(...) before arming
the diagnostics waiter; change the sequence so the diagnostic waiter is created
before sending didOpen or simply replace the pair with
client.open_and_wait(tmp_path / "leaf.cppm") to atomically open and wait for
diagnostics; locate the calls to client.open, client.wait_for_diagnostics and
event.wait in tests/integration/test_modules.py and either call
client.wait_for_diagnostics(...) first and then client.open(...), or swap both
lines for a single client.open_and_wait(...) call to reliably avoid the race.
🧹 Nitpick comments (1)
tests/integration/test_modules.py (1)

32-56: Avoid generating CMake output inside the checked-in fixtures.

Lines 37-56 create build/ and copy compile_commands.json back into the source tree under tests/data/modules/*. That leaves these fixtures stateful across runs and dirties the worktree after local test execution. The tmp_path pattern already used in test_save_recompile is safer here too—copy each fixture into a per-test temp dir before invoking CMake.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/test_modules.py` around lines 32 - 56, The helper
_generate_cdb is creating build/ and copying compile_commands.json back into the
original fixture workspace, which dirties checked-in fixtures; instead, update
the tests to copy the fixture directory into a per-test temporary directory
(follow the tmp_path usage in test_save_recompile) and call _generate_cdb on
that temp copy so CMake runs and output stay isolated, and modify _generate_cdb
so it only writes compile_commands.json inside the provided workspace (or
returns the path to the generated compile_commands.json) without mutating the
original fixture; ensure the tests invoke the temp-copy pattern before calling
_generate_cdb and remove the shutil.copy2 back into source-tree behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/integration/test_modules.py`:
- Around line 400-402: The test reintroduces an open/diagnostics race by calling
client.open(...) before arming the diagnostics waiter; change the sequence so
the diagnostic waiter is created before sending didOpen or simply replace the
pair with client.open_and_wait(tmp_path / "leaf.cppm") to atomically open and
wait for diagnostics; locate the calls to client.open,
client.wait_for_diagnostics and event.wait in tests/integration/test_modules.py
and either call client.wait_for_diagnostics(...) first and then
client.open(...), or swap both lines for a single client.open_and_wait(...) call
to reliably avoid the race.

---

Nitpick comments:
In `@tests/integration/test_modules.py`:
- Around line 32-56: The helper _generate_cdb is creating build/ and copying
compile_commands.json back into the original fixture workspace, which dirties
checked-in fixtures; instead, update the tests to copy the fixture directory
into a per-test temporary directory (follow the tmp_path usage in
test_save_recompile) and call _generate_cdb on that temp copy so CMake runs and
output stay isolated, and modify _generate_cdb so it only writes
compile_commands.json inside the provided workspace (or returns the path to the
generated compile_commands.json) without mutating the original fixture; ensure
the tests invoke the temp-copy pattern before calling _generate_cdb and remove
the shutil.copy2 back into source-tree behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6963b6ed-f1a5-4315-9349-328c052065b5

📥 Commits

Reviewing files that changed from the base of the PR and between e5e60f5 and 217982e.

📒 Files selected for processing (2)
  • tests/conftest.py
  • tests/integration/test_modules.py

@16bit-ykiko

Copy link
Copy Markdown
Member Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown
✅ Actions performed

Reviews resumed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
tests/integration/test_modules.py (1)

399-402: ⚠️ Potential issue | 🟠 Major

Arm the diagnostics wait before opening leaf.cppm.

Line 400 reintroduces the same missed-notification race: if diagnostics arrive immediately after client.open(...), the event created on Line 401 never gets set and the test can hang intermittently. Reuse open_and_wait() here, or create the event before calling open().

🔧 Suggested patch
-    leaf_uri, _ = client.open(tmp_path / "leaf.cppm")
-    event = client.wait_for_diagnostics(leaf_uri)
-    await asyncio.wait_for(event.wait(), timeout=60.0)
+    leaf_uri, _ = await client.open_and_wait(tmp_path / "leaf.cppm")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/test_modules.py` around lines 399 - 402, The test risks a
missed-notification race because it calls client.open(...) before arming the
diagnostics wait; change the sequence to arm the diagnostics listener first
(call client.wait_for_diagnostics(...) or reuse the helper open_and_wait() that
both opens and waits) so the Event is created before invoking client.open for
"leaf.cppm" (refer to client.open, client.wait_for_diagnostics, and
open_and_wait) ensuring the event can be set even if diagnostics arrive
immediately.
🧹 Nitpick comments (1)
tests/integration/test_modules.py (1)

59-64: Avoid the fixed post-initialize sleep.

Line 63 makes every module test pay a hard-coded 2s and can still race on slower runners. Since CliceClient already records work-done progress, prefer waiting on a concrete “CDB scan finished” signal instead of sleeping.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/test_modules.py` around lines 59 - 64, Replace the fixed
asyncio.sleep(2.0) in the _init function with a deterministic wait for the CDB
scan work-done progress recorded by CliceClient: after calling
client.initialize(workspace) poll or await the client's work-done/progress API
(e.g., client.wait_for_work_done, client.wait_for_progress_completion, or loop
until client.work_done_records shows the "CDB scan finished" entry) and return
only once that specific "CDB scan finished" progress is observed, ensuring tests
don't rely on a hard-coded sleep.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/integration/test_modules.py`:
- Around line 399-402: The test risks a missed-notification race because it
calls client.open(...) before arming the diagnostics wait; change the sequence
to arm the diagnostics listener first (call client.wait_for_diagnostics(...) or
reuse the helper open_and_wait() that both opens and waits) so the Event is
created before invoking client.open for "leaf.cppm" (refer to client.open,
client.wait_for_diagnostics, and open_and_wait) ensuring the event can be set
even if diagnostics arrive immediately.

---

Nitpick comments:
In `@tests/integration/test_modules.py`:
- Around line 59-64: Replace the fixed asyncio.sleep(2.0) in the _init function
with a deterministic wait for the CDB scan work-done progress recorded by
CliceClient: after calling client.initialize(workspace) poll or await the
client's work-done/progress API (e.g., client.wait_for_work_done,
client.wait_for_progress_completion, or loop until client.work_done_records
shows the "CDB scan finished" entry) and return only once that specific "CDB
scan finished" progress is observed, ensuring tests don't rely on a hard-coded
sleep.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c5d0a091-31f0-4dd9-942e-f54c91c336ce

📥 Commits

Reviewing files that changed from the base of the PR and between e5e60f5 and 217982e.

📒 Files selected for processing (2)
  • tests/conftest.py
  • tests/integration/test_modules.py

16bit-ykiko and others added 4 commits March 31, 2026 13:10
…port

The previous CMakeLists.txt used a simple add_library(OBJECT) which
doesn't enable C++20 module compilation. Use cmake 3.28+ FILE_SET
CXX_MODULES so the generated CDB includes correct module flags.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The server finds compile_commands.json in the build/ subdirectory
automatically, no need to copy it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…port

Use FILE_SET CXX_MODULES in all test CMakeLists.txt so cmake generates
module-aware compile commands (-fmodules-ts, -fmodule-mapper, etc.).
Also add .cppm to format-cpp task and apply clang-format to test data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…t setup

Replace manual `test_data_dir / "subdir"` paths with a `@pytest.mark.workspace("subdir")`
marker and `ws` fixture that auto-resolves the workspace path and generates CDB via CMake.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
tests/integration/test_modules.py (1)

351-353: ⚠️ Potential issue | 🟠 Major

Arm diagnostics wait before opening leaf.cppm to avoid flakiness.

On Line 351, client.open(...) happens before the waiter is created on Line 352. If diagnostics arrive immediately, the event is missed and this can intermittently timeout.

🔧 Proposed fix
-    leaf_uri, _ = client.open(tmp_path / "leaf.cppm")
-    event = client.wait_for_diagnostics(leaf_uri)
-    await asyncio.wait_for(event.wait(), timeout=60.0)
+    leaf_uri, _ = await client.open_and_wait(tmp_path / "leaf.cppm")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/test_modules.py` around lines 351 - 353, The test creates
the diagnostics waiter after opening the file which can miss immediately
arriving diagnostics; swap the operations so you call
client.wait_for_diagnostics(...) to get the event before calling
client.open(tmp_path / "leaf.cppm") and then await the event (i.e., use the
event returned by client.wait_for_diagnostics for leaf_uri/filename prior to
invoking client.open) to ensure the test reliably observes diagnostics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/integration/test_modules.py`:
- Around line 351-353: The test creates the diagnostics waiter after opening the
file which can miss immediately arriving diagnostics; swap the operations so you
call client.wait_for_diagnostics(...) to get the event before calling
client.open(tmp_path / "leaf.cppm") and then await the event (i.e., use the
event returned by client.wait_for_diagnostics for leaf_uri/filename prior to
invoking client.open) to ensure the test reliably observes diagnostics.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2b4b287d-4f16-405d-be3b-d7e2619770c2

📥 Commits

Reviewing files that changed from the base of the PR and between 8fa3e11 and abf08c7.

📒 Files selected for processing (5)
  • tests/conftest.py
  • tests/integration/test_file_operation.py
  • tests/integration/test_lifecycle.py
  • tests/integration/test_modules.py
  • tests/integration/test_server.py
✅ Files skipped from review due to trivial changes (1)
  • tests/integration/test_file_operation.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/integration/test_lifecycle.py
  • tests/integration/test_server.py

16bit-ykiko and others added 3 commits March 31, 2026 13:56
Merge workspace resolution and LSP initialization into the client fixture
so tests no longer need manual `await client.initialize()` calls. Rename
`ws` to `workspace` and add type annotations throughout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixture types are inferred by pytest; annotations in test cases are redundant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes Pylance type inference for the yielding client fixture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
tests/conftest.py (2)

209-222: Consider caching the CDB generation to avoid redundant CMake runs.

The workspace fixture runs generate_cdb() on every test that uses the same workspace directory. For workspaces with CMakeLists.txt, this could redundantly regenerate compile_commands.json multiple times during a test session. Consider checking if build/compile_commands.json already exists before regenerating.

♻️ Proposed optimization
 `@pytest.fixture`
 def workspace(request: pytest.FixtureRequest, test_data_dir: Path) -> Path | None:
     """Resolve workspace path from `@pytest.mark.workspace`("subdir") marker.

     If the workspace contains a CMakeLists.txt, automatically runs cmake
     to generate compile_commands.json. Returns None if no marker is present.
     """
     marker = request.node.get_closest_marker("workspace")
     if marker is None:
         return None
     path = test_data_dir / marker.args[0]
-    if (path / "CMakeLists.txt").exists():
+    cdb_path = path / "build" / "compile_commands.json"
+    if (path / "CMakeLists.txt").exists() and not cdb_path.exists():
         generate_cdb(path)
     return path
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/conftest.py` around lines 209 - 222, The workspace fixture currently
calls generate_cdb(path) whenever a CMakeLists.txt exists, causing redundant
CMake runs; modify the logic in the workspace fixture to first check for an
existing compile_commands.json (e.g. path / "build" / "compile_commands.json")
and only call generate_cdb(path) if that file is missing or stale, keeping the
existing checks around (path / "CMakeLists.txt"). Ensure you reference the
workspace fixture and the generate_cdb function when implementing the presence
check so repeated tests sharing the same workspace skip unnecessary
regeneration.

186-206: Consider checking for Ninja availability alongside CMake.

generate_cdb requires both cmake and ninja to be available (since it uses -G Ninja). If Ninja is missing, CMake will fail with a confusing error. Consider validating Ninja availability upfront for a clearer error message.

💡 Proposed improvement
 def generate_cdb(workspace: Path) -> None:
     """Generate compile_commands.json using CMake with Ninja backend."""
     cmake = shutil.which("cmake")
     if cmake is None:
         raise RuntimeError("cmake executable not found in PATH")
+    ninja = shutil.which("ninja")
+    if ninja is None:
+        raise RuntimeError("ninja executable not found in PATH (required for -G Ninja)")
     subprocess.run(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/conftest.py` around lines 186 - 206, The generate_cdb function
currently only verifies CMake; add a check for Ninja availability by calling
shutil.which("ninja") before invoking cmake and raise a clear RuntimeError if
it's missing so users get an immediate, helpful message (update generate_cdb to
validate both cmake and ninja and include the tool names in the error text).
tests/integration/test_server.py (1)

284-293: Unused workspace parameter.

The workspace parameter is declared but never used in test_hover_on_unknown_file. While the marker is required for the fixture to work, consider if this test actually needs a workspace or if it could be restructured.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/test_server.py` around lines 284 - 293, The
test_hover_on_unknown_file function declares an unused workspace parameter;
either remove the workspace parameter from the test signature (keep the
`@pytest.mark.workspace`("hello_world") decorator if that marker must remain) or
rename the parameter to _workspace to signal it is intentionally unused; update
the function signature for test_hover_on_unknown_file accordingly to eliminate
the unused-parameter warning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/conftest.py`:
- Around line 209-222: The workspace fixture currently calls generate_cdb(path)
whenever a CMakeLists.txt exists, causing redundant CMake runs; modify the logic
in the workspace fixture to first check for an existing compile_commands.json
(e.g. path / "build" / "compile_commands.json") and only call generate_cdb(path)
if that file is missing or stale, keeping the existing checks around (path /
"CMakeLists.txt"). Ensure you reference the workspace fixture and the
generate_cdb function when implementing the presence check so repeated tests
sharing the same workspace skip unnecessary regeneration.
- Around line 186-206: The generate_cdb function currently only verifies CMake;
add a check for Ninja availability by calling shutil.which("ninja") before
invoking cmake and raise a clear RuntimeError if it's missing so users get an
immediate, helpful message (update generate_cdb to validate both cmake and ninja
and include the tool names in the error text).

In `@tests/integration/test_server.py`:
- Around line 284-293: The test_hover_on_unknown_file function declares an
unused workspace parameter; either remove the workspace parameter from the test
signature (keep the `@pytest.mark.workspace`("hello_world") decorator if that
marker must remain) or rename the parameter to _workspace to signal it is
intentionally unused; update the function signature for
test_hover_on_unknown_file accordingly to eliminate the unused-parameter
warning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cc135eae-6df9-40be-83c3-2b24ffee27d7

📥 Commits

Reviewing files that changed from the base of the PR and between abf08c7 and 9c435b1.

📒 Files selected for processing (5)
  • tests/conftest.py
  • tests/integration/test_file_operation.py
  • tests/integration/test_lifecycle.py
  • tests/integration/test_modules.py
  • tests/integration/test_server.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/integration/test_modules.py

…remove pyproject.toml

- Switch to pytest-asyncio auto mode with @pytest.fixture instead of
  @pytest_asyncio.fixture, enabling Pylance type inference for fixtures
- Remove pyproject.toml in favor of pytest.ini (managed by pixi.toml)
- Remove redundant section separators and docstrings that duplicate
  test names, keep meaningful ones for module-related tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@16bit-ykiko

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@16bit-ykiko 16bit-ykiko changed the title refactor(tests): unify TempFile/TempDir, extract shared helpers refactor(tests): CMake-based CDB, workspace fixture, test cleanup Mar 31, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/conftest.py (1)

225-244: Add return type annotation to the client fixture.

The async generator fixture is missing a return type annotation. Adding it improves IDE support and documentation.

Suggested fix
 `@pytest.fixture`
 async def client(
     request: pytest.FixtureRequest, executable: Path, workspace: Path | None
-):
+) -> AsyncGenerator[CliceClient, None]:
     """Spawn clice server, auto-initialize if `@pytest.mark.workspace` is present."""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/conftest.py` around lines 225 - 244, The async pytest fixture function
client is missing a return type annotation; update the fixture signature for
client to include an appropriate return type (e.g., ->
AsyncGenerator[CliceClient, None] or -> CliceClient depending on whether it
yields) to improve IDE typing and docs, and ensure the annotation matches how
client is used (if using yield-style fixture use AsyncGenerator with proper
typing, otherwise annotate as CliceClient); reference the client fixture and the
CliceClient class and keep the start_io and initialize calls unchanged.
tests/integration/test_modules.py (1)

7-7: Consider using relative import for consistency.

Using from tests.conftest import generate_cdb works but from conftest import generate_cdb (relative) is more conventional for imports within the same test package and matches how pytest resolves conftest.

Suggested change
-from tests.conftest import generate_cdb
+from conftest import generate_cdb
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/test_modules.py` at line 7, Replace the absolute import in
the test with a relative import for consistency: change the import that brings
in generate_cdb (currently written as from tests.conftest import generate_cdb)
to use the package-local conftest module (from conftest import generate_cdb) so
pytest's usual conftest resolution is used and imports are consistent across
tests; update the import statement where generate_cdb is referenced in
test_modules.py accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/conftest.py`:
- Around line 225-244: The async pytest fixture function client is missing a
return type annotation; update the fixture signature for client to include an
appropriate return type (e.g., -> AsyncGenerator[CliceClient, None] or ->
CliceClient depending on whether it yields) to improve IDE typing and docs, and
ensure the annotation matches how client is used (if using yield-style fixture
use AsyncGenerator with proper typing, otherwise annotate as CliceClient);
reference the client fixture and the CliceClient class and keep the start_io and
initialize calls unchanged.

In `@tests/integration/test_modules.py`:
- Line 7: Replace the absolute import in the test with a relative import for
consistency: change the import that brings in generate_cdb (currently written as
from tests.conftest import generate_cdb) to use the package-local conftest
module (from conftest import generate_cdb) so pytest's usual conftest resolution
is used and imports are consistent across tests; update the import statement
where generate_cdb is referenced in test_modules.py accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2105941f-1ecf-426c-96ff-ed6b9e70f51d

📥 Commits

Reviewing files that changed from the base of the PR and between abf08c7 and b89b8b8.

📒 Files selected for processing (7)
  • tests/conftest.py
  • tests/integration/test_file_operation.py
  • tests/integration/test_lifecycle.py
  • tests/integration/test_modules.py
  • tests/integration/test_server.py
  • tests/pyproject.toml
  • tests/pytest.ini
💤 Files with no reviewable changes (1)
  • tests/pyproject.toml
✅ Files skipped from review due to trivial changes (1)
  • tests/pytest.ini

16bit-ykiko and others added 2 commits March 31, 2026 14:47
- Disable CMAKE_CXX_SCAN_FOR_MODULES to avoid "compiler does not
  support scanning" error on macOS
- Prefer clang++ as CXX compiler when available (pixi environment)
- Show cmake stderr on failure for easier debugging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
UNDEFINED_SYMBOL is on line 5 (0-indexed line 4), not line 3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/conftest.py (2)

217-222: Consider validating marker arguments for a clearer error message.

If a test uses @pytest.mark.workspace() without an argument, marker.args[0] will raise an unhelpful IndexError. A guard would provide a clearer failure message for test developers.

💡 Proposed validation
     marker = request.node.get_closest_marker("workspace")
     if marker is None:
         return None
+    if not marker.args:
+        raise ValueError("@pytest.mark.workspace requires a subdirectory argument")
     path = test_data_dir / marker.args[0]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/conftest.py` around lines 217 - 222, The workspace marker handling
should validate that the marker includes an argument before accessing
marker.args[0]; update the block that calls
request.node.get_closest_marker("workspace") to check marker is not None and
that len(marker.args) > 0, and if not raise a clear error (e.g.,
pytest.UsageError or ValueError) explaining that `@pytest.mark.workspace` requires
a path argument; keep the existing behavior of constructing path = test_data_dir
/ marker.args[0] and calling generate_cdb(path) when a CMakeLists.txt exists.

226-229: Consider adding return type annotation for the async fixture.

The fixture is well-designed—auto-initializing when @pytest.mark.workspace is present while allowing manual initialization for custom scenarios (like test_save_recompile). Adding a return type would improve IDE support.

💡 Proposed type annotation
 `@pytest.fixture`
 async def client(
     request: pytest.FixtureRequest, executable: Path, workspace: Path | None
-):
+) -> AsyncGenerator[CliceClient, None]:
     """Spawn clice server, auto-initialize if `@pytest.mark.workspace` is present."""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/conftest.py` around lines 226 - 229, The async pytest fixture named
client should include an explicit return type to improve IDE/type-checker
support; add a return annotation such as -> AsyncGenerator[ClientType, None] (or
the concrete client class used in your tests) on the client fixture, import
AsyncGenerator (from typing or collections.abc depending on Python version) and
the concrete ClientType, and ensure the annotation matches how the fixture
yields/returns the client instance in client.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/conftest.py`:
- Around line 217-222: The workspace marker handling should validate that the
marker includes an argument before accessing marker.args[0]; update the block
that calls request.node.get_closest_marker("workspace") to check marker is not
None and that len(marker.args) > 0, and if not raise a clear error (e.g.,
pytest.UsageError or ValueError) explaining that `@pytest.mark.workspace` requires
a path argument; keep the existing behavior of constructing path = test_data_dir
/ marker.args[0] and calling generate_cdb(path) when a CMakeLists.txt exists.
- Around line 226-229: The async pytest fixture named client should include an
explicit return type to improve IDE/type-checker support; add a return
annotation such as -> AsyncGenerator[ClientType, None] (or the concrete client
class used in your tests) on the client fixture, import AsyncGenerator (from
typing or collections.abc depending on Python version) and the concrete
ClientType, and ensure the annotation matches how the fixture yields/returns the
client instance in client.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3379d5c0-cd91-446a-9233-2d76c178e72d

📥 Commits

Reviewing files that changed from the base of the PR and between b89b8b8 and 40c3450.

📒 Files selected for processing (1)
  • tests/conftest.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/integration/test_modules.py`:
- Around line 263-267: Replace the flaky fixed sleep by waiting for a
deterministic server response: remove the await asyncio.sleep(5.0) after
client.open(workspace / "cycle_a.cppm") and instead wait for a concrete signal
such as calling client.open_and_wait(workspace / "cycle_a.cppm") or polling
client.diagnostics for the corresponding URI with a bounded timeout; ensure you
use the same workspace / "cycle_a.cppm" URI and propagate a clear timeout so the
test fails fast if the server doesn't respond.
- Around line 200-203: The test currently assumes the first diagnostic
(diags[0]) is the error; instead iterate/search through diags to find any
diagnostic whose range.start.line equals the expected line (and optionally
matches the expected severity) and assert that such a diagnostic exists; replace
the direct use of diags[0] and the equality assertion with a check like using
any(...) or next(filter(...), None) over diags and assert the result is not None
(and then assert its properties if needed) so the test no longer depends on
diagnostic ordering (refer to the diags variable and the error_diag usage in
this block).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a853c941-262b-4867-9303-a9862ab63e66

📥 Commits

Reviewing files that changed from the base of the PR and between 40c3450 and 8555072.

📒 Files selected for processing (1)
  • tests/integration/test_modules.py

Comment thread tests/integration/test_modules.py Outdated
Comment thread tests/integration/test_modules.py
The pixi clang++ needs lld as linker but doesn't use it by default.
Reuse cmake/toolchain.cmake to get correct compiler and linker setup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/conftest.py`:
- Around line 215-221: The code directly indexes marker.args[0] which can raise
IndexError or produce unclear failures; before using marker.args in the fixture
containing request.node.get_closest_marker("workspace"), validate that
marker.args exists, has at least one positional argument, and that the first
argument is a valid string (or path-like), and if not raise a clear test-setup
error (e.g., pytest.fail or raise a ValueError with an explanatory message).
Move the assignment path = test_data_dir / marker.args[0] and the subsequent
CMakeLists check/generate_cdb call to after this validation so you never index
marker.args without checking.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fea3687f-4d38-4880-8bdf-f9df36e05375

📥 Commits

Reviewing files that changed from the base of the PR and between 8555072 and c135864.

📒 Files selected for processing (1)
  • tests/conftest.py

Comment thread tests/conftest.py
16bit-ykiko and others added 2 commits March 31, 2026 15:39
clang-cl does not support CMake's module dependency scanning.
Disabling it still generates correct CDB entries on all platforms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Unify all platforms to use clang++/lld. This enables CMake's C++20
module dependency scanning on Windows, which clang-cl did not support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
tests/conftest.py (1)

216-222: ⚠️ Potential issue | 🟡 Minor

Validate @pytest.mark.workspace arguments before indexing.

Line 219 assumes one positional string argument. @pytest.mark.workspace with no args (or wrong type) will throw unclear setup errors. Please guard args and raise pytest.UsageError with a clear message; also validate that the resolved path exists.

Suggested hardening
 marker = request.node.get_closest_marker("workspace")
 if marker is None:
     return None
-path = test_data_dir / marker.args[0]
+if len(marker.args) != 1 or not isinstance(marker.args[0], str):
+    raise pytest.UsageError(
+        '@pytest.mark.workspace requires one string argument, e.g. '
+        '@pytest.mark.workspace("modules/hello_world")'
+    )
+path = (test_data_dir / marker.args[0]).resolve()
+if not path.exists():
+    raise pytest.UsageError(f"Workspace path does not exist: {path}")
 if (path / "CMakeLists.txt").exists():
     generate_cdb(path)
 return path
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/conftest.py` around lines 216 - 222, The workspace marker handling
should validate marker arguments and the target path before using them: in the
block that calls request.node.get_closest_marker("workspace") check that
marker.args exists, has exactly one positional argument, and that the argument
is a string; if not, raise pytest.UsageError with a clear message about the
expected `@pytest.mark.workspace`("rel/path") usage. After resolving path =
test_data_dir / marker.args[0], verify path.exists() (and is a directory if
appropriate) and raise pytest.UsageError if the path does not exist; only then
call generate_cdb(path) if (path / "CMakeLists.txt").exists().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/conftest.py`:
- Around line 122-125: wait_diagnostics can miss diagnostics that arrived just
before calling wait_for_diagnostics because wait_for_diagnostics re-arms state;
modify wait_diagnostics to first check self.diagnostics.get(uri) (or equivalent
current diagnostics storage) and return immediately if diagnostics are already
present for that URI, otherwise call wait_for_diagnostics(uri) and await
event.wait(); ensure you reference the existing symbols wait_diagnostics,
wait_for_diagnostics, self.diagnostics, uri, and event.wait() so the guard check
is added before re-arming/waiting.

---

Duplicate comments:
In `@tests/conftest.py`:
- Around line 216-222: The workspace marker handling should validate marker
arguments and the target path before using them: in the block that calls
request.node.get_closest_marker("workspace") check that marker.args exists, has
exactly one positional argument, and that the argument is a string; if not,
raise pytest.UsageError with a clear message about the expected
`@pytest.mark.workspace`("rel/path") usage. After resolving path = test_data_dir /
marker.args[0], verify path.exists() (and is a directory if appropriate) and
raise pytest.UsageError if the path does not exist; only then call
generate_cdb(path) if (path / "CMakeLists.txt").exists().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bfd25d0f-aa87-4e49-84ad-365be1d0355f

📥 Commits

Reviewing files that changed from the base of the PR and between c135864 and b7c1fa2.

📒 Files selected for processing (1)
  • tests/conftest.py

Comment thread tests/conftest.py
16bit-ykiko and others added 3 commits March 31, 2026 15:50
- Guard wait_diagnostics against already-arrived diagnostics
- Validate @pytest.mark.workspace arguments
- Use any() for diagnostic assertion to avoid ordering dependency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch Windows toolchain from clang-cl to clang++ (GNU driver) with
--target=x86_64-pc-windows-msvc. This enables CMake C++20 module
scanning support on Windows (clang-cl not supported until CMake 4.4).

Simplify ASan setup: clang++ handles -fsanitize=address linking
automatically on all platforms, removing the need for manual
clang_rt.asan_dynamic lib specification on Windows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep the manual ASan runtime lib linking for clang-cl (MSVC driver),
use -fsanitize=address linker flag for clang++/gcc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
cmake/toolchain.cmake (2)

7-8: Hardcoded x86_64 target limits to 64-bit Windows builds.

The --target=x86_64-pc-windows-msvc flag hardcodes the architecture. If ARM64 Windows support is needed in the future, this would need parameterization. If 64-bit only is intentional, this is fine.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmake/toolchain.cmake` around lines 7 - 8, The flags set for C and C++ are
hardcoded to "--target=x86_64-pc-windows-msvc" which forces 64-bit Windows only;
change the set(CMAKE_C_FLAGS ...) and set(CMAKE_CXX_FLAGS ...) usage to use a
configurable variable (e.g., TARGET_TRIPLE or WINDOWS_TARGET_TRIPLE) or derive
the triple from CMake variables (like CMAKE_SYSTEM_PROCESSOR or an option) and
default to the current value, then use that variable in the set() calls and
expose it as a CACHE STRING so ARM64 (or other) targets can be selected without
editing the file.

9-11: Linker flag inconsistency with CMakeLists.txt.

The toolchain sets -fuse-ld=lld for Windows, but CMakeLists.txt (lines 85-88) adds -fuse-ld=lld-link via target_link_options for the WIN32 case. When targeting the MSVC ABI (--target=x86_64-pc-windows-msvc), lld-link is the appropriate linker driver.

While the last -fuse-ld= flag typically wins, having both is inconsistent and potentially confusing. Consider using -fuse-ld=lld-link here for clarity, or removing these from the toolchain and relying solely on CMakeLists.txt.

Suggested change for consistency
-    set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=lld" CACHE STRING "Executable linker flags")
-    set(CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=lld" CACHE STRING "Shared library linker flags")
-    set(CMAKE_MODULE_LINKER_FLAGS "-fuse-ld=lld" CACHE STRING "Module linker flags")
+    set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=lld-link" CACHE STRING "Executable linker flags")
+    set(CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=lld-link" CACHE STRING "Shared library linker flags")
+    set(CMAKE_MODULE_LINKER_FLAGS "-fuse-ld=lld-link" CACHE STRING "Module linker flags")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmake/toolchain.cmake` around lines 9 - 11, Toolchain sets -fuse-ld=lld which
conflicts with CMakeLists.txt adding -fuse-ld=lld-link for WIN32; update
consistency by using lld-link for MSVC targets or remove duplication. Modify the
three linker flag settings in toolchain.cmake (the set(CMAKE_EXE_LINKER_FLAGS
...), set(CMAKE_SHARED_LINKER_FLAGS ...), set(CMAKE_MODULE_LINKER_FLAGS ...)) to
use "-fuse-ld=lld-link" when targeting the MSVC ABI
(--target=x86_64-pc-windows-msvc) or alternatively remove these sets and rely on
the WIN32-specific target_link_options in CMakeLists.txt that adds
"-fuse-ld=lld-link"; ensure the chosen approach keeps CMakeLists.txt's
target_link_options(WIN32 ... "-fuse-ld=lld-link") behavior unchanged.
tests/conftest.py (1)

190-209: Consider validating workspace path stays within test data directory.

While the workspace path originates from test markers (controlled by developers), there's no validation that the resolved path stays within test_data_dir. A marker like @pytest.mark.workspace("../../etc") would resolve outside the expected directory.

The static analysis hint (S603) about subprocess is a false positive here since shell=False is used and the path comes from test fixtures, not external input.

Optional path containment check
     path = test_data_dir / marker.args[0]
+    try:
+        path = path.resolve()
+        path.relative_to(test_data_dir.resolve())
+    except ValueError:
+        raise pytest.UsageError(
+            f"Workspace path must be within test data directory: {path}"
+        )
     if (path / "CMakeLists.txt").exists():
         generate_cdb(path)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/conftest.py` around lines 190 - 209, The generate_cdb function should
validate that the provided workspace path stays inside the repository test data
directory before invoking subprocess: resolve workspace (workspace_resolved =
workspace.resolve()) and resolve the test data root (e.g. test_data_dir =
Path(__file__).resolve().parent / "test_data" or the existing test_data_dir
fixture/constant), then check containment using
workspace_resolved.is_relative_to(test_data_dir.resolve()) (or compare parts for
older Python) and raise a RuntimeError if not contained; perform this check at
the start of generate_cdb (before calling cmake/subprocess) so you reject
markers like "@pytest.mark.workspace('../../etc')" early.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CMakeLists.txt`:
- Around line 55-58: The else() branch appends ASan to CMAKE_EXE_LINKER_FLAGS
and CMAKE_SHARED_LINKER_FLAGS but omits CMAKE_MODULE_LINKER_FLAGS; update the
else() block to also append " -fsanitize=address" to CMAKE_MODULE_LINKER_FLAGS
so module libraries receive the same ASan linker flag as executables and shared
libs (mirror the clang-cl branch's handling of CMAKE_MODULE_LINKER_FLAGS).

---

Nitpick comments:
In `@cmake/toolchain.cmake`:
- Around line 7-8: The flags set for C and C++ are hardcoded to
"--target=x86_64-pc-windows-msvc" which forces 64-bit Windows only; change the
set(CMAKE_C_FLAGS ...) and set(CMAKE_CXX_FLAGS ...) usage to use a configurable
variable (e.g., TARGET_TRIPLE or WINDOWS_TARGET_TRIPLE) or derive the triple
from CMake variables (like CMAKE_SYSTEM_PROCESSOR or an option) and default to
the current value, then use that variable in the set() calls and expose it as a
CACHE STRING so ARM64 (or other) targets can be selected without editing the
file.
- Around line 9-11: Toolchain sets -fuse-ld=lld which conflicts with
CMakeLists.txt adding -fuse-ld=lld-link for WIN32; update consistency by using
lld-link for MSVC targets or remove duplication. Modify the three linker flag
settings in toolchain.cmake (the set(CMAKE_EXE_LINKER_FLAGS ...),
set(CMAKE_SHARED_LINKER_FLAGS ...), set(CMAKE_MODULE_LINKER_FLAGS ...)) to use
"-fuse-ld=lld-link" when targeting the MSVC ABI
(--target=x86_64-pc-windows-msvc) or alternatively remove these sets and rely on
the WIN32-specific target_link_options in CMakeLists.txt that adds
"-fuse-ld=lld-link"; ensure the chosen approach keeps CMakeLists.txt's
target_link_options(WIN32 ... "-fuse-ld=lld-link") behavior unchanged.

In `@tests/conftest.py`:
- Around line 190-209: The generate_cdb function should validate that the
provided workspace path stays inside the repository test data directory before
invoking subprocess: resolve workspace (workspace_resolved =
workspace.resolve()) and resolve the test data root (e.g. test_data_dir =
Path(__file__).resolve().parent / "test_data" or the existing test_data_dir
fixture/constant), then check containment using
workspace_resolved.is_relative_to(test_data_dir.resolve()) (or compare parts for
older Python) and raise a RuntimeError if not contained; perform this check at
the start of generate_cdb (before calling cmake/subprocess) so you reject
markers like "@pytest.mark.workspace('../../etc')" early.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3fa6fb40-b529-47de-9914-6b6c0041002a

📥 Commits

Reviewing files that changed from the base of the PR and between b7c1fa2 and ce1aabd.

📒 Files selected for processing (5)
  • CMakeLists.txt
  • cmake/toolchain.cmake
  • scripts/activate_asan.bat
  • tests/conftest.py
  • tests/integration/test_modules.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/integration/test_modules.py

Comment thread CMakeLists.txt
16bit-ykiko and others added 2 commits March 31, 2026 16:11
…ndows

- Toolchain: remove if/else duplication, Windows only needs extra
  CMAKE_MSVC_RUNTIME_LIBRARY (pixi clang already targets MSVC implicitly)
- ASan: use CMAKE_CXX_COMPILER_FRONTEND_VARIANT to distinguish clang-cl
  (MSVC frontend, needs manual ASan lib linking) from clang++ (GNU
  frontend, -fsanitize=address handles linking automatically)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
lld-link enables Identical COMDAT Folding by default, which merges
string literals like "}" and L"}" causing ASan to report ODR violations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@16bit-ykiko
16bit-ykiko merged commit bc04845 into main Mar 31, 2026
17 checks passed
@16bit-ykiko
16bit-ykiko deleted the feat/compile-index-tests branch March 31, 2026 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant